<?php

namespace App\Models;

use App\BtApi;
use App\FileManege;
use App\Helper;
use App\SSHApi;
use Exception;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\SoftDeletes;
use Illuminate\Support\Facades\DB;
use Illuminate\Support\Facades\Storage;
use SoapClient;
use SoapParam;

class Server extends Model
{
    use SoftDeletes;

    protected $table = 'server';

    protected $fillable = ['user_id', 'name', 'domain',
        'server_host', 'server_port', 'auth_port',
        'db_host', 'db_port',
        'db_acc', 'db_pwd',
        'info', 'qq_server', 'qq_group', 'remark',
        'soap_host', 'soap_port',
        'soap_acc', 'soap_pwd',
        'ext',
        'bt_url', 'bt_key',
        'path',
        'configPath',
        'luaPath',
        'root_pwd',
        'type'
    ];

    protected $appends = ['enable', 'enableDays'];

    public $BtApi = null;
//    protected $configPath='/docker/worldserver/etc/worldserver.conf';
//    protected $luaPath='/docker/worldserver/bin/lua_scripts';

//    public function getPathAttribute($value)
//    {
//        return $value??'/azerothcore-wotlk';
//    }

//    public function getConfigPathAttribute($value)
//    {
//        return $value??'/docker/worldserver/etc/worldserver.conf';
//    }
//
//    public function getLuaPathAttribute($value)
//    {
//        return $value??'/docker/worldserver/bin/lua_scripts';
//    }

//    public function getServerHostAttribute($value)
//    {
//        return $this->machine?$this->machine->ip:$value;
//    }
    public $fileManege = null;

    public function __construct(array $attributes = [])
    {
        parent::__construct($attributes);
    }

    public function getDomain()
    {
        return $this->domain . '.' . env('DOMAIN_PLAYER');
    }

    /**
     * @return FileManege
     */
    public function GetFileManege(): FileManege
    {
        if ($this->fileManege != null) return $this->fileManege;
        if ($this->type === 1) {
            $fm = new BtApi($this->bt_url, $this->bt_key);
        } else {
            $fm = new SSHApi($this->server_host, $this->root_port ?? 22, 'root', $this->root_pwd);
        }
        return $fm;
    }

    public function __call($method, $parameters)
    {
        $this->initBtApi();
        if ($method == 'getWorldServerConf') {
            return $this->BtApi->GetFileBody($this->path . $this->configPath);
        } elseif ($method == 'saveWorldServerConf') {
            return $this->BtApi->SaveFileBody($this->path . $this->configPath, $parameters[0]);
        } elseif ($method == 'getLuaList') {
            return $this->BtApi->GetDir($this->path . $this->luaPath);
        } elseif ($method == 'createLuaDir') {
            return $this->BtApi->CreateDir($this->path . $this->luaPath);
        } elseif ($method == 'createLuaFile') {
            return $this->BtApi->CreateFile($this->path . $this->luaPath . '/' . $parameters[0]);
        } elseif ($method == 'saveLuaFile') {
            return $this->BtApi->SaveFileBody($this->path . $this->luaPath . '/' . $parameters[0], $parameters[1]);
        } elseif ($method == 'delLuaFile') {
            return $this->BtApi->DeleteFile($this->path . $this->luaPath . '/' . $parameters[0]);
        } elseif ($method == 'changeLuaName') {

        } elseif ($method == 'changeLuaStatus') {
            return $this->BtApi->MvFile($this->path . $this->luaPath . '/', $parameters[0], $parameters[1]);
        } elseif ($method == 'renameLuaFile') {
            return $this->BtApi->MvFile($this->path . $this->luaPath . '/', $parameters[0], $parameters[1]);
        } elseif ($method == 'getLuaScript') {
            return $this->BtApi->GetFileBody($this->path . $this->luaPath . '/' . $parameters[0]);
        } elseif ($method == 'setLuaScript') {
            return $this->BtApi->SaveFileBody($this->path . $this->luaPath . '/' . $parameters[0], $parameters[1]);
        } else {
            return parent::__call($method, $parameters);
        }
    }

    protected function initBtApi()
    {
        if (!$this->BtApi) {
            $this->BtApi = new BtApi($this->bt_url, $this->bt_key);
        }
    }

    public function luaList()
    {
        $res = $this->getLuaList();
        if (isset($res['msg']) && $res['msg'] == '指定目录不存在!') {
            $this->createLuaDir();
            $res = $this->getLuaList();
        }
        $files = [];
        if ($res['FILES']) {
            foreach ($res['FILES'] as $file) {
                $arr = explode(';', $file);
                $arrName = explode('.', $arr[0]);
                $files[] = [
                    'name' => $arrName[0],
                    'type' => $arrName[1],
                ];
            }
        }

        return $files;
    }

    public function user()
    {
        return $this->belongsTo('App\Models\User', 'user_id', 'id');
    }

    public function goods()
    {
        return $this->hasMany(Goods::class, 'server_id', 'id')->orderBy('order', 'desc');
    }

    public function goodsCategory()
    {
        return $this->hasMany(GoodsCategory::class, 'server_id', 'id');
    }

    public function videos()
    {
        return $this->hasMany(Video::class, 'server_id', 'id')->orderBy('sort', 'desc');
    }

    public function launcherConfig()
    {
        $count = LauncherConfig::where('server_id', $this->id)->count();
        if ($count == 0) {
            $lc = new LauncherConfig();
            $lc->server_id = $this->id;
            $lc->save();
        }
        return $this->hasOne(LauncherConfig::class, 'server_id', 'id');
    }

    public function spellMall()
    {
        return $this->hasOne(ServerSpellMall::class, 'server_id', 'id');
    }

    public function machine()
    {
        return $this->hasOne(Machine::class, 'server_id', 'id');
    }

    public function getVersionAttribute($value)
    {
        return is_null($value) ? 0 : $value;
    }

    public function getEnableAttribute()
    {
        if (!$this->endTime) {
            return false;
        }
        if ($this->endTime && $this->endTime < now() && !config('conf.serverFree')) {
            return false;
        } else {
            return true;
        }
    }

    public function getEnableDaysAttribute()
    {
        if (!$this->endTime) {
            return -1;
        }
        $res = date_diff(date_create($this->endTime), now());
        return $res->days * ($res->invert == 1 ? 1 : -1);
    }

    public function getExtAttribute($value)
    {
        $data = json_decode($value, true);
        if ($data){
            if (!key_exists('showRank',$data)) $data['showRank']=1;
            if (!key_exists('showMoney',$data)) $data['showMoney']=1;
            if (!key_exists('showRecommend',$data)) $data['showRecommend']=1;
            if (!key_exists('showMall',$data)) $data['showMall']=1;
//        if (!key_exists('showSpell',$data)) $data['showSpell']=1;
        }

        return $data;
    }

    public function getConfAttribute()
    {
        return json_decode($this->world_server_conf, true);
    }
//    public function setExtAttribute($value)
//    {
//        return json_encode($value);
//    }

    /**
     * [isExpire 判断服务端是否到期]
     * @Author   Wuhaojie
     * @DateTime 2019-10-26T09:46:21+0800
     * @return   boolean                  [description]
     */
    public function isExpire()
    {
        $time = strtotime($this->endTime);
        if ($time < time()) {
            return true;
        } else {
            return false;
        }
    }

    public function CreateLauncher()
    {
        $lPath = "server/{$this->id}/Launcher/";
        Storage::deleteDirectory($lPath);
        Storage::put($lPath . "Launcher.exe.config", $this->getConfigStr());
        $ver = config('conf.LauncherVer');
        Storage::copy("Launcher{$ver}.exe", $lPath . "Launcher.exe");
        $zip = new \ZipArchive(); //首先实例化这个类
        if ($zip->open(storage_path("app/{$lPath}Launcher.zip"), \ZipArchive::CREATE | \ZipArchive::OVERWRITE) === TRUE) { //然后查看是否存在test.zip这个压缩包
            $zip->addFile(storage_path("app/{$lPath}Launcher.exe"), $this->name . ".exe");
            $zip->addFile(storage_path("app/{$lPath}Launcher.exe.config"), $this->name . ".exe.config"); //将too.php和test.php两个文件添加到test.zip压缩包中
            $zip->close(); //关闭
        }

        //初始化配置数据
//        $this->launcherConfig;

        return $this->launcherConfig;
    }

    public function DownLoadLauncher()
    {
        $lPath = "server/{$this->id}/Launcher/";
        $ver = config('conf.LauncherVer');
        if (!Storage::exists($lPath . "Launcher.exe") || !Storage::exists($lPath . "Launcher.zip") ||
            md5_file(storage_path("app/{$lPath}Launcher.exe")) != md5_file(storage_path("app/Launcher{$ver}.exe"))
        ) {
            $this->CreateLauncher();
        }
        return response()->download(storage_path("app/{$lPath}Launcher.zip"), $this->name . ".zip");
    }

    public function getConfigStr()
    {
        $strTmp = file_get_contents(storage_path('app/Launcher.exe.config'));
        $str = str_replace("{domain}", $this->getDomain(), $strTmp);
        $str = str_replace("{ip}", $this->server_host, $str);
        return $str;
    }


    public function ReloadEluna(&$res, &$err)
    {
        return $this->RunCommand('.reload eluna', $res, $err);
    }

    public function ServerInfo(&$res, &$err)
    {
        return $this->RunCommand('.server info', $res, $err);
    }
//    public function ReloadConfig(&$res,&$err){
//        return $this->RunCommand('.reload config',$res,$err);
//    }

//    /**
//     * @param $playerName
//     * @param $subject
//     * @param $text
//     * @param $itemId
//     * @param $count
//     * @param $res
//     * @param $err
//     */
//    public function SendItemToPlay($playerName,$subject,$text,$itemId,$count, &$res,&$err){
//        $command= '.send items '.$playerName.' "'.$subject.'" "'.$text.'" '.$itemId.':'.$count;
//        return $this->RunCommand($command,$res,$err);
//    }
//
//    /**
//     * @param $playerName
//     * @param $subject
//     * @param $text
//     * @param $count
//     * @param $res
//     * @param $err
//     */
//    public function SendMoneyToPlay($playerName,$subject,$text, $count, &$res,&$err){
//        $command= '.send money '.$playerName.' "'.$subject.'" "'.$text.'" '.$count;
//        return $this->RunCommand($command,$res,$err);
//    }

    public function RunCommand($COMMAND, &$res, &$err)
    {

        $soapInfo = [
            'soap_uri' => 'urn:AC',
//            'soap_uri' => 'urn:MaNGOS',
            'soap_host' => $this->soap_host,
            'soap_port' => $this->soap_port,
            'soap_acc' => $this->soap_acc,
            'soap_pwd' => $this->soap_pwd
        ];

        try {
            $conn = new SoapClient(NULL, array(
                'location' => 'http://' . $soapInfo['soap_host'] . ':' . $soapInfo['soap_port'] . '/',
                'uri' => $soapInfo['soap_uri'],
                'style' => SOAP_RPC,
                'login' => $soapInfo['soap_acc'],
                'password' => $soapInfo['soap_pwd']
            ));
            $res = $conn->executeCommand(new SoapParam($COMMAND, 'command'));
            unset($conn);
        } catch (Exception $e) {
            $err = "Have error on soap: " . $e->getMessage();

            if (strpos($e, 'There is no such command') !== false) {
                $err = 'There is no such command!';
            }
        }
    }

    public function checkBTAPI()
    {
        $bt = new BtApi($this->bt_url, $this->bt_key);
        $data = $bt->GetSystemTotal();
        return ['res' => 0, 'data' => $data];
    }

    public function statusBt()
    {
        try {
            $res = $this->checkBTAPI();
//            return $res;
            if (isset($res['data']['version'])) {
                return 1;
            } else {
                return str_replace('59.47.231.138', '0.0.0.0', $res['data']['msg']);
            }
        } catch (Exception $e) {
            return '连接失败';
        }
    }

    public function statusSSH()
    {
        return -1;
    }

    public function statusDB()
    {
        try {
            $conn = mysqli_connect($this->db_host, $this->db_acc, $this->db_pwd, '', $this->db_port);
            return 1;
        } catch (Exception $e) {
            return $e->getMessage();
        }
    }

    public function statusSOAP()
    {
        $res = null;
        $err = null;
        $this->RunCommand('server info', $res, $err);
        if ($err == null)
            return 1;
        else
            return 2;
    }

    public function resetPwd()
    {
        $pwd = Helper::getRandStr(12);
        $flags = [0, 0, $pwd];
        try {
            $conn = mysqli_connect($this->db_host, $this->db_acc, $this->db_pwd, '', $this->db_port);
            $changed = null;

            $version = $conn->server_version;
            if ($version > 80000) {
                $sql = "ALTER user 'root'@'%' IDENTIFIED by '$pwd'";
                $changed = mysqli_query($conn, $sql);
            } else {
                $sql = "update mysql.user set `Password`=password('{$pwd}') where `User`='root' and `Host` ='%'";
                $changed = mysqli_query($conn, $sql);
                $changed = mysqli_query($conn, "FLUSH PRIVILEGES");
            }
            mysqli_close($conn);
            $this->db_pwd = $pwd;
            $flags[0] = $changed ? 1 : 2;
        } catch (Exception $e) {
            $flags[0] = 2;
        }

        $res = null;
        $err = null;
        $this->RunCommand("account set password admin $pwd $pwd", $res, $err);
        if ($err == null) {
            $this->soap_pwd = $pwd;
            $flags[1] = 1;
        } else
            $flags[1] = 2;

        $this->save();

        return $flags;
    }

    public function isOld(): bool
    {
        Helper::setDataBaseConf('mysql_world', $this, 'acore_world');
        try {
            $exists = DB::connection('mysql_world')->table('version_db_world')->exists();
            return true;
        } catch (\Exception $e) {
            return false;
        }
    }

    public function getVersion(): int
    {
        Helper::setDataBaseConf('mysql_world', $this, 'acore_world');
        $ver = DB::connection('mysql_world')->select("select `core_version` from `version`");
        $version = explode(' ', $ver[0]->core_version)[3];
        $version = substr($version, 0, 10);
        $version = str_replace('-', '', $version);
        return is_numeric($version)?$version:0;
    }

    public function getLauncherUrl()
    {
        return ($this->ext && $this->ext['loginUrl']) ? $this->ext['loginUrl'] : '/dlq';
    }

}
